/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *   
 * Description :
 * 
 * 				^ y
 * 				|
 * 			2º	|	1º
 * 				|
 *  -x 	-----------------> x
 * 				|
 * 			3º	|   4º
 * 				|
 * 				| -y
 */

package unb.wikinarua.geopartitura.draw;

import unb.wikinarua.geopartitura.Main;
import unb.wikinarua.geopartitura.model.Vibration;
import unb.wikinarua.geopartitura.util.Constants;
import unb.wikinarua.geopartitura.view.DrawView;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Join;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.os.Message;
import android.util.Log;

public class Line {

	private Main mainContext;
	private int index;

	private float amplitude = 5;
	private double period = Math.PI;

	private float oldX = 0;
	private float oldY = 0;
	private float newX;
	private float newY;
	private float x;
	private float y;

	private float rotation = 0f;
	private float size = 100f;
	private float sizePD = 0.5f;

	private float k = 10;
	private float w = 0.5f;
	private float decay = 0.9f;

	private float width, height;

	private boolean vibrate = false;

	private Canvas canvas;
	private Paint paint;
	private Path path = new Path();
	private int color = Color.WHITE;
	private boolean isCoordUpdated;

	public Line(float newX, float newY, Paint paint) {
		this.newX = newX;
		this.newY = -1 * newY;

		this.x = newX;
		this.y = -1 * newY; 

		this.paint = paint;

//		this.rotation = (float) Math.toDegrees(Math.atan2(-1 * newY, newX));

		if (newX == 0)
			this.sizePD = Math.abs(newY);
		else if (newY == 0)
			this.sizePD = Math.abs(newX);
		else
			this.sizePD = (float) Math.hypot(newX, newY);

		this.vibrate = true;
		this.isCoordUpdated = false;
		this.decay = 0.1f;
	}

	public Line(float newX, float newY, Paint paint, int color, Main main, int index) {
		this.mainContext = main;
		this.index = index + 1;

		this.newX = newX;
		this.newY = -1 * newY;

		this.x = newX;
		this.y = -1 * newY; 

		this.paint = paint;

//		this.rotation = (float) Math.toDegrees(Math.atan2(-1 * newY, newX));

		if (newX == 0)
			this.sizePD = Math.abs(newY);
		else if (newY == 0)
			this.sizePD = Math.abs(newX);
		else
			this.sizePD = (float) Math.hypot(newX, newY);

		this.vibrate = true;
		this.isCoordUpdated = false;
		this.decay = 0.1f;

		this.color = color;
	}

	public void draw() {
		float time = DrawView.FRAMECOUNT;
		float increment = (float) period / 100;

		canvas.save();
		canvas.translate(canvas.getWidth() / 2, canvas.getHeight() / 2);

		paint.setAntiAlias(true);
		paint.setStyle(Paint.Style.FILL_AND_STROKE);
		paint.setStrokeWidth(DrawView.STROKE_WIDTH);
		paint.setStrokeJoin(Join.ROUND);
		paint.setColor(Color.WHITE);

		path.reset();
		canvas.drawCircle(0, 0, 16, paint);

		paint.setStyle(Paint.Style.STROKE);
		path.addCircle(0, 0, 16, Direction.CW);
		path.moveTo(0, 0);
		oldX = 0;
		oldY = 0;

		canvas.rotate(rotation);

		if (vibrate) {
			for (float i = 0; i < period; i += increment) {
				plotNewLineAt(i, time);
			}
			plotNewLineAt((float) period, time);
			amplitude *= decay;

			sendMsg(amplitude);
		} else {
			path.lineTo(newX, newY);
		}

		canvas.drawPath(path, paint);

		paint.setColor(color);
		paint.setStyle(Paint.Style.FILL_AND_STROKE);
		canvas.drawCircle(newX, newY, 8, paint);

		canvas.restore();

		if ((int) amplitude == 0) {
			sendMsg(0);
			vibrate = false;
			amplitude = 5f;
			decay = 0.9f;
		}
	}

	public float calculateTrigonometricSumToProductIdentity(float posX,
			float time) {
		float y;
		y = (float) (amplitude * Math.sin(k * posX - w * time) + amplitude
				* Math.sin(k * posX + w * time));
		return y;
	}

	public void plotNewLineAt(float i, float time) {
		newX = (float) (size / period) * i;
		newY = calculateTrigonometricSumToProductIdentity(i, time);

		path.lineTo(newX, newY);
	}

	private void sendMsg(float amp) {
		Message msg = new Message();
		msg.what = Main.VIBRATE_LINE;
		msg.obj = new Vibration(index, sizePD, amp / 5);
		mainContext.handler.sendMessage(msg);
	}

	public void fitCoordinate() {
		if (!isCoordUpdated) {
			Log.i(Constants.TAG,"Normalized x:" + x + " y:" + y);
			this.x *= ((width-150)/2) + 50 ;
			this.y *= ((height-150)/2) + 50;
			Log.i(Constants.TAG,"UnNormalized x:" + x + " y:" + y);
			if (x == 0) {
				this.size = Math.abs(x);
				}
			else if (y == 0) {
				this.size = Math.abs(y); 
				}
			else {
				this.size = (float) Math.hypot(x, y);
				}
			Log.i(Constants.TAG,"UnNormalized Size: " + size);
			this.newX = x;
			this.newY = y;
			this.rotation = (float) Math.toDegrees(Math.atan2(newY, newX));
			isCoordUpdated = true;
		}
	}

	/** Getters / Setters */
	public float getAmplitude() {
		return amplitude;
	}

	public void setAmplitude(float amplitude) {
		this.amplitude = amplitude;
	}

	public double getPeriod() {
		return period;
	}

	public void setPeriod(double period) {
		this.period = period;
	}

	public float getOldX() {
		return oldX;
	}

	public void setOldX(float oldX) {
		this.oldX = oldX;
	}

	public float getOldY() {
		return oldY;
	}

	public void setOldY(float oldY) {
		this.oldY = oldY;
	}

	public float getNewX() {
		return newX;
	}

	public void setNewX(float newX) {
		this.newX = newX;
	}

	public float getNewY() {
		return newY;
	}

	public void setNewY(float newY) {
		this.newY = newY;
	}

	public float getK() {
		return k;
	}

	public void setK(float k) {
		this.k = k;
	}

	public float getW() {
		return w;
	}

	public void setW(float w) {
		this.w = w;
	}

	public float getDecay() {
		return decay;
	}

	public void setDecay(float decay) {
		this.decay = decay;
	}

	public boolean isVibrate() {
		return vibrate;
	}

	public void setVibrate(boolean vibrate) {
		this.vibrate = vibrate;
	}

	public float getRotation() {
		return rotation;
	}

	public void setRotation(float rotation) {
		this.rotation = rotation;
	}

	public float getSize() {
		return size;
	}

	public void setSize(float size) {
		this.size = size;
	}

	public float getX() {
		return x;
	}

	public void setX(float x) {
		this.x = x;
	}

	public float getY() {
		return y;
	}

	public void setY(float y) {
		this.y = y;
	}

	public Canvas getCanvas() {
		return canvas;
	}

	public void setCanvas(Canvas canvas) {
		this.canvas = canvas;
		this.width = canvas.getWidth();
		this.height = canvas.getHeight();
		fitCoordinate();
	}

	public Paint getPaint() {
		return paint;
	}

	public void setPaint(Paint paint) {
		this.paint = paint;
	}

}
